<template>
  <a-modal
    :width="'1000px'"
    :visible="visible"
    :destroyOnClose="true"
    :maskClosable="false"
    :keyboard="false"
    wrapClassName="hv-dark-dialog"
    :title="title"
    @cancel="handleCancel"
  >
    <template #footer>
      <a-button @click="handleCancel">取消</a-button>
      <a-button type="primary" @click="handleOk" v-if="!readonly"> 确定</a-button>
    </template>
    <div class="hv-json-input-dialog-codemirror">
      <codemirror
        :modelValue="formatting()"
        :placeholder="placeholder"
        :style="{ height: height }"
        :autofocus="true"
        :indent-with-tab="true"
        :tab-size="tabSize"
        :lint="true"
        :disabled="readonly"
        :extensions="extensions"
        @update:modelValue="updateValue"
        @ready="handleReady"
        @update="handleStateUpdate"
        ref="codemirrorRef"
      />
    </div>
    <div class="hv-json-input-dialog-footer">
      <div class="hv-json-input-dialog-footer-buttons">
        <template v-if="!readonly">
          <a-button type="primary" size="small" @click="handleUndo" ghost>
            <hv-dynamic-icon iconName="arrow-left-outlined" /> 还原
          </a-button>
          <a-button type="primary" size="small" @click="handleRedo" ghost>
            重做 <hv-dynamic-icon iconName="arrow-right-outlined" />
          </a-button>
        </template>
      </div>
      <div class="hv-json-input-dialog-footer-infos">
        <div>空格: {{ tabSize }}</div>
        <div>长度: {{ state.length }}</div>
        <div>行数: {{ state.lines }}</div>
        <div>光标: {{ state.cursor }}</div>
        <div>行中: {{ state.selected }}</div>
      </div>
    </div>
  </a-modal>
</template>

<script setup lang="ts">
import { Codemirror } from "vue-codemirror";
import { json } from "@codemirror/lang-json";
import { oneDark } from "@codemirror/theme-one-dark";
import { redo, undo } from "@codemirror/commands";
import { type PropType, reactive, ref, shallowRef } from "vue";
import type { EditorView, ViewUpdate } from "@codemirror/view";
import { notification } from "ant-design-vue";
import type DataSourceGroupType from "@/visual/commons/types/data-source-group-type";
import { httpRequestMethodConst } from "@/visual/commons/consts/dashboard-const";

const props = defineProps({
  readonly: {
    type: Boolean,
    default: false,
  },
  visible: {
    type: Boolean,
    default: false,
  },
  dataSource: {
    type: Object as PropType<DataSourceGroupType>,
    default: () => {
      return {
        type: "",
        jsonConfig: {
          data: "{}",
        },
        httpRequestConfig: {
          method: httpRequestMethodConst.get,
          autoReload: false,
          reloadTime: 60000,
          url: "",
          params: {},
          resultHandle: '(data) => data',
        },
      };
    },
  },
  tabSize: {
    type: Number,
    default: 2,
  },
  title: {
    type: String,
    default: "编辑数据",
  },
  placeholder: {
    type: String,
    default: "请输入JSON...",
  },
  height: {
    type: String,
    default: "360px",
  },
});

const emit = defineEmits(["update:visible", "update:dataSource"]);

const codemirrorVal = ref<string>("");
const editorView = shallowRef<EditorView>();
const state = reactive({
  lines: null as null | number,
  cursor: null as null | number,
  selected: null as null | number,
  length: null as null | number,
});

const extensions = [json(), oneDark];

const handleReady = ({ view }: any) => {
  editorView.value = view;
};

const handleStateUpdate = (viewUpdate: ViewUpdate) => {
  const ranges = viewUpdate.state.selection.ranges;
  state.selected = ranges.reduce((plus, range) => plus + range.to - range.from, 0);
  state.cursor = ranges[0].anchor;
  state.length = viewUpdate.state.doc.length;
  state.lines = viewUpdate.state.doc.lines;
};

const handleUndo = () => {
  undo({
    state: editorView.value!.state,
    dispatch: editorView.value!.dispatch,
  });
};
const handleRedo = () => {
  redo({
    state: editorView.value!.state,
    dispatch: editorView.value!.dispatch,
  });
};

const validateJson = (val: string) => {
  try {
    return JSON.parse(val);
  } catch (e: any) {
    console.error(e.message);
    return null;
  }
};

const formatting = () => {
  const json = validateJson(props.dataSource.jsonConfig.data);
  if (null !== json) {
    return JSON.stringify(json, null, "\t");
  } else {
    return "";
  }
};

const updateValue = (val) => {
  codemirrorVal.value = val;
};

/**
 * 取消
 */
const handleCancel = () => {
  emit("update:visible", false);
};

const handleOk = () => {
  if (codemirrorVal.value && codemirrorVal.value.length) {
    const json = validateJson(codemirrorVal.value);
    if (null !== json) {
      const dataSourceData = {
        ...props.dataSource,
      };

      dataSourceData.jsonConfig = {
        data: codemirrorVal.value,
      };
      emit("update:dataSource", dataSourceData);
      handleCancel();
    } else {
      notification.warning({
        message: "提示",
        description: "数据格式不正确，请检查！",
      });
    }
  } else {
    handleCancel();
  }
};
</script>

<style lang="less" scoped>
@import "./JsonInputDialog.less";
</style>
